package com.haircut.project.service.finance.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.haircut.project.dao.finance.FinanceStaticMapper;
import com.haircut.project.model.finance.FinanceStatic;
import com.haircut.project.service.finance.IStaticFinanceService;
import com.haircut.project.utils.record.RecordUtils;
import com.haircut.project.vo.finance.StaticDeptFinaceReqVo;
import com.haircut.project.vo.finance.StaticDeptFinaceVo;
import com.haircut.sys.dao.dept.SyDeptMapper;
import com.haircut.sys.model.dept.SysDept;
import com.modular.constant.NumberConstant;
import com.modular.redis.RedisUtils;
import com.modular.result.PageParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;

@Service
public class StaticFinanceServiceImpl implements IStaticFinanceService {
    @Autowired
    private FinanceStaticMapper financeStaticMapper;
    @Autowired
    private SyDeptMapper syDeptMapper;
    /**
     * 异步处理，每次只处理10条
     */
    private final Semaphore semaphore = new Semaphore(1);
    int pageSize = NumberConstant.HUNDRED * NumberConstant.NINE;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    @Qualifier("modularSnowflake")
    private Snowflake snowflake;
    private final Logger logger = LoggerFactory.getLogger(StaticFinanceServiceImpl.class);
    /**
     * 按天统计数据
     */
    public void staticCurrDay(String redisKey)
    {
        Date currDate = DateUtil.date();
        StaticDeptFinaceReqVo reqVo = new StaticDeptFinaceReqVo();
        reqVo.setStartDate(DateUtil.beginOfDay(currDate));
        reqVo.setEndDate(DateUtil.endOfDay(currDate));
        calcByDeptePage(reqVo,redisKey);
    }

    /**
     * 统计一个月
     */
    public void staticWeekDay(String redisKey)
    {
        Date endDate = DateUtil.yesterday();
        Date startDate = DateUtil.offsetDay(endDate,-7);
        StaticDeptFinaceReqVo reqVo = new StaticDeptFinaceReqVo();
        reqVo.setStartDate(DateUtil.beginOfDay(startDate));
        reqVo.setEndDate(DateUtil.endOfDay(endDate));
        calcByDeptePage(reqVo,redisKey);
    }

    /**
     * 根据部门统计数据
     * @param reqVo
     * @param redisKey
     */
    private void calcByDeptePage(StaticDeptFinaceReqVo reqVo,String redisKey){
        boolean hasNext = Boolean.TRUE;
        int currPage = NumberConstant.ZERO;
        IPage<?> pageReq = null;
        Page<SysDept> deptPage = null;
        PageParams reqPage = new PageParams();
        reqPage.setPageSize(pageSize);

        while(hasNext){
            redisUtils.flushRedis(redisKey);
            currPage ++;
            reqPage.setPageNum(currPage);
            pageReq = Page.of(currPage,pageSize,Boolean.FALSE);
            deptPage = syDeptMapper.findCalcDeptPage(pageReq,reqPage);
            if(ObjectUtil.isNull(deptPage) || CollUtil.isEmpty(deptPage.getRecords())){
                break;
            }
            if(deptPage.getRecords().size() < currPage){
                hasNext = Boolean.FALSE;
            }
            deptPage.getRecords().parallelStream().forEach(p->{
                logger.info("calcByDeptePage deptId {},deptName {} str",p.getId(),p.getDeptName());
                long s = System.currentTimeMillis();
                try{
                    semaphore.acquire();
                    StaticDeptFinaceReqVo tempReqVo = new StaticDeptFinaceReqVo();
                    BeanUtils.copyProperties(reqVo,tempReqVo);
                    tempReqVo.setDeptId(p.getId());
                    calcDeptBatchFinance(redisKey,tempReqVo,p);
                }catch (Exception e){
                    logger.error(StrUtil.format("calcByDeptePage err deptId {} deptName {}",p.getId(),p.getDeptName()),e);
                }finally {
                    semaphore.release();
                    logger.info("calcByDeptePage deptId {},deptName {} time {}",p.getId(),p.getDeptName(),(System.currentTimeMillis()-s));
                }
            });
        }
    }

    /**
     * 按照部门计算数据
     * @param redisKey
     * @param reqVo
     */
    private void calcDeptBatchFinance(String redisKey,StaticDeptFinaceReqVo reqVo,SysDept dept){
        if(ObjectUtil.isNull(dept.getShareRatio())){
            return;
        }

        redisUtils.flushRedis(redisKey);
        List<StaticDeptFinaceVo> dbList = financeStaticMapper.findDayStatic(reqVo);
        if(CollUtil.isEmpty(dbList)){
            return;
        }
        Map<String, FinanceStatic> dbMap = findByStatic(reqVo);
        FinanceStatic financeStatic = null;
        Date currDate = DateUtil.date();
        for(StaticDeptFinaceVo vo:dbList){
            String key = StrUtil.format("{}_{}",vo.getStaticDay(),vo.getDeptId());
            financeStatic = dbMap.get(key);
            if(ObjectUtil.isNull(financeStatic)){
                financeStatic = new FinanceStatic();
            }
            financeStatic.setStaticDate(currDate);
            caclFinance(vo,financeStatic,dept);
            financeStatic.setDeptId(dept.getId());
            financeStatic.setShareRatio(dept.getShareRatio());
            if(StrUtil.isBlank(financeStatic.getId())){
                financeStatic.setId(snowflake.nextIdStr());
                financeStaticMapper.insert(financeStatic);
            }else{
                financeStaticMapper.updateById(financeStatic);
            }

        }

    }


    /**
     * 计算数据
     * @param vo
     * @param financeStatic
     */
    private void caclFinance(StaticDeptFinaceVo vo,FinanceStatic financeStatic,SysDept dept){
        financeStatic.setStaticDay(vo.getStaticDay());
        financeStatic.setCompanyCardAmount(Optional.of(vo.getCompanyCardAmount()).orElse(BigDecimal.ZERO));
        financeStatic.setCompanyCashAmount(Optional.of(vo.getCompanyCashAmount()).orElse(BigDecimal.ZERO));
        BigDecimal total = financeStatic.getCompanyCardAmount().add(financeStatic.getCompanyCashAmount());
        financeStatic.setTotalAmount(total);
        financeStatic.setCurrCardAmount(RecordUtils.calcAmountRate(financeStatic.getCompanyCardAmount(),dept.getShareRatio()));
        financeStatic.setCurrCashAmount(RecordUtils.calcAmountRate(financeStatic.getCompanyCashAmount(),dept.getShareRatio()));
        financeStatic.setParentCardAmount(financeStatic.getCompanyCardAmount().subtract(financeStatic.getCurrCardAmount()));
        financeStatic.setParentCashAmount(financeStatic.getCompanyCashAmount().subtract(financeStatic.getCurrCashAmount()));

    }

    /**
     * 查询数据库中的数据
     * @param reqVo
     * @return
     */
    private Map<String, FinanceStatic> findByStatic(StaticDeptFinaceReqVo reqVo){
        List<FinanceStatic> financeStaticList = financeStaticMapper.findDbStaticFinance(reqVo);
        if(CollUtil.isEmpty(financeStaticList)){
            return Collections.emptyMap();
        }
        return financeStaticList.stream().collect(Collectors.toMap(k->StrUtil.format("{}_{}",k.getStaticDay(),k.getDeptId()),p->p,(v1,v2)->v1));
    }


}
